﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using WeiboSharp.Extensions;
using WeiboSharp.Models;

namespace WeiboSharp
{
    partial class Http : IHttp
    {
        protected bool HasParameters
        {
            get
            {
                return Parameters.Any();
            }
        }

        /// <summary>
        /// True if this HTTP request has any HTTP cookies
        /// </summary>
        protected bool HasCookies
        {
            get
            {
                return Cookies.Any();
            }
        }

        /// <summary>
        /// True if a request body has been specified
        /// </summary>
        protected bool HasBody
        {
            get
            {
                return !string.IsNullOrEmpty(RequestBody);
            }
        }

        /// <summary>
        /// True if files have been set to be uploaded
        /// </summary>
        protected bool HasFiles
        {
            get
            {
                return Files.Any();
            }
        }

        /// <summary>
        /// UserAgent to be sent with request
        /// </summary>
        public string UserAgent { get; set; }
        /// <summary>
        /// Timeout in milliseconds to be used for the request
        /// </summary>
        public int Timeout { get; set; }
        /// <summary>
        /// System.Net.ICredentials to be sent with request
        /// </summary>
        public ICredentials Credentials { get; set; }
        /// <summary>
        /// Collection of files to be sent with request
        /// </summary>
        public List<FileParameter> Files { get; private set; }

        /// <summary>
        /// Whether or not HTTP 3xx response redirects should be automatically followed
        /// </summary>
        public bool FollowRedirects { get; set; }

        /// <summary>
        /// Maximum number of automatic redirects to follow if FollowRedirects is true
        /// </summary>
        public int? MaxRedirects { get; set; }

        /// <summary>
        /// HTTP headers to be sent with request
        /// </summary>
        public List<Parameter> Headers { get; private set; }
        /// <summary>
        /// HTTP parameters (QueryString or Form values) to be sent with request
        /// </summary>
        public List<Parameter> Parameters { get; private set; }
        /// <summary>
        /// HTTP cookies to be sent with request
        /// </summary>
        public List<HttpCookie> Cookies { get; private set; }
        /// <summary>
        /// Request body to be sent with request
        /// </summary>
        public string RequestBody { get; set; }
        /// <summary>
        /// Content type of the request body.
        /// </summary>
        public string RequestContentType { get; set; }
        /// <summary>
        /// URL to call for this request
        /// </summary>
        public Uri Url { get; set; }
        /// <summary>
        /// Proxy info to be sent with request
        /// </summary>
        public IWebProxy Proxy { get; set; }

        public Http()
        {
            Headers = new List<Parameter>();
            Files = new List<FileParameter>();
            Parameters = new List<Parameter>();
            Cookies = new List<HttpCookie>();
        }

        private HttpWebRequest PreparePostData(HttpWebRequest webRequest)
        {
            if (HasFiles)
            {
                webRequest.ContentType = GetMultipartFormContentType();
                WriteMultipartFormData(webRequest);
            }

            return PreparePostBody(webRequest);
        }

        private HttpWebRequest AppendHeaders(HttpWebRequest webRequest)
        {
            foreach (var header in Headers)
            {
                webRequest.Headers.Add(header.Name, header.Value);
            }
            return webRequest;
        }

        private string FormBoundary = DateTime.Now.Ticks.ToString("x");
        private string GetMultipartFormContentType()
        {
            return string.Format("multipart/form-data; boundary={0}", FormBoundary);
        }

        private string GetMultipartFileHeader(FileParameter file)
        {
            return string.Format("--{0}{4}Content-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"{4}Content-Type: {3}{4}{4}",
                FormBoundary, file.Name, file.FileName, file.ContentType ?? "application/octet-stream", Environment.NewLine);
        }

        private string GetMultipartFormData(Parameter param)
        {
            return string.Format("--{0}{3}Content-Disposition: form-data; name=\"{1}\"{3}{3}{2}{3}",
                FormBoundary, param.Name, param.Value, Environment.NewLine);
        }

        private string GetMultipartFooter()
        {
            return string.Format("{1}--{0}--{1}", FormBoundary, Environment.NewLine);
        }
        //private string GetMultipartFormContentType()
        //{
        //    return string.Format("multipart/form-data; boundary={0}", FormBoundary);
        //}

        //private string GetMultipartFileHeader(FileParameter file)
        //{
        //    return string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{3}Content-Type: {2}{3}{3}",
        //       file.Name, file.FileName, file.ContentType ?? "application/octet-stream", Environment.NewLine);
        //}

        //private string GetMultipartFormData(Parameter param)
        //{
        //    return string.Format("{3}--{0}{3}Content-Disposition: form-data; name=\"{1}\"{3}{3}{2}",
        //        FormBoundary, param.Name, param.Value.ToUtf8String(), Environment.NewLine);
        //}

        //private string GetMultipartFooter()
        //{
        //    return string.Format("--{0}--{1}", FormBoundary, Environment.NewLine);
        //}

        private WeiboResponse ExtractResponseData(HttpWebResponse webResponse)
        {
            var response = new WeiboResponse();

            using (webResponse)
            {
                response.ContentEncoding = webResponse.ContentEncoding;
                response.ContentType = webResponse.ContentType;
                response.ContentLength = webResponse.ContentLength;
                response.RawBytes = webResponse.GetResponseStream().ReadAsBytes();
                response.Content = GetString(response.RawBytes);
                response.StatusCode = webResponse.StatusCode;
                response.StatusDescription = webResponse.StatusDescription;
                response.ResponseUri = webResponse.ResponseUri;
            }

            return response;
        }

        private WeiboResponse ExtractResponseData(HttpWebRequest request)
        {
            using (var webResponse = (HttpWebResponse)request.GetResponse())
            {
                return ExtractResponseData(webResponse);
            }
        }

        public string GetString(byte[] buffer)
        {
            if (buffer == null || buffer.Length == 0)
                return "";

            // Ansi as default
            Encoding encoding = Encoding.UTF8;

            /*
                EF BB BF		UTF-8 
                FF FE UTF-16	little endian 
                FE FF UTF-16	big endian 
                FF FE 00 00		UTF-32, little endian 
                00 00 FE FF		UTF-32, big-endian 
                */

            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
            {
                encoding = Encoding.UTF8;
            }
            else if (buffer[0] == 0xfe && buffer[1] == 0xff)
            {
                encoding = Encoding.Unicode;
            }
            else if (buffer[0] == 0xfe && buffer[1] == 0xff)
            {
                encoding = Encoding.BigEndianUnicode; // utf-16be
            }

            else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
            {
                encoding = Encoding.UTF32;
            }
            else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
            {
                encoding = Encoding.UTF7;
            }

            using (var stream = new MemoryStream())
            {
                stream.Write(buffer, 0, buffer.Length);
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream, encoding))
                {
                    return reader.ReadToEnd();
                }
            }
        }
        private HttpWebRequest PreparePostBody(HttpWebRequest webRequest)
        {
            if (HasFiles)
            {
                webRequest.ContentType = GetMultipartFormContentType();
            }
            else if (HasParameters)
            {
                webRequest.ContentType = "application/x-www-form-urlencoded";
                RequestBody = EncodeParameters();
            }
            else if (HasBody)
            {
                webRequest.ContentType = RequestContentType;
            }
            return webRequest;
        }

        private string EncodeParameters()
        {
            var querystring = new StringBuilder();
            foreach (var p in Parameters)
            {
                if (querystring.Length > 1)
                    querystring.Append("&");
                querystring.AppendFormat("{0}={1}", p.Name.UrlEncode(), p.Value.UrlEncode());
            }

            return querystring.ToString();
        }
    }
}
